﻿using System;
using TaiwuModFramework.Common.Info;
using System.Threading;
using TaiwuModFramework.Common.Base;
using TaiwuModFramework.Common.Utils;

namespace TaiwuModFramework.Common.Abstract {
    public abstract class AReqClient {

        public abstract ReqBuilder Req(string modIdStr, string reqPath);


        public abstract object Send(ReqInfo req);
        public abstract object Send(ReqInfo req, out RespInfo resp);
        public abstract void SendAsync(ReqInfo req, Action<RespInfo> respHandler);



        public class ReqBuilder {
            private BaseReqClient client;
            private string modIdStr;
            private string reqPath;
            private Type[] argTypes = null;
            private object[] args = null;
            private Action<RespInfo> ReturnHandler;

            protected internal ReqBuilder(BaseReqClient client, string modIdStr, string reqPath) {
                if(modIdStr == null || reqPath == null) throw new Exception("模组ID和请求路径不能为空");
                this.reqPath = reqPath;
                this.modIdStr = modIdStr;
                this.client = client;
            }

            /// <summary>
            /// 设置参数类型，会清空已有的参数与参数类型
            /// </summary>
            /// <param name="types"></param>
            /// <returns></returns>
            public ReqBuilder SetArgTypes(params Type[] types) {
                // 检查是否有null
                foreach(var type in types) if(type == null) throw new Exception("参数类型列表中存在空值");
                argTypes = types;
                return this;
            }
            public ReqBuilder SetArgs(params object[] args) {
                if(argTypes == null) {
                    //throw new Exception("未配置参数类型");
                    argTypes = new Type[args.Length];
                    for(int i = 0; i < args.Length; i++) {
                        var arg = args[i];
                        if(arg == null) throw new Exception($"第{i}个参数与类型都为空");
                        argTypes[i] = arg.GetType();
                    }
                    this.args = args;
                    return this;
                }
                if(argTypes.Length != args.Length) throw new Exception("参数数目与设置的参数类型数目不一致");
                for(int i = 0; i < args.Length; i++) {
                    var type = argTypes[i];
                    if(type == null && args[i] == null) throw new Exception($"第{i}个参数与类型都为空");
                    if(!type.IsAssignableFrom(args[i].GetType())) throw new Exception($"第{i}个参数不能转为{argTypes[i].FullName}类型的实例");
                }
                this.args = args;
                return this;
            }
            public ReqBuilder SetReturnHandler(Action<RespInfo> handler) {
                this.ReturnHandler = handler;
                return this;
            }
            public ReqBuilder SetClient(BaseReqClient client) {
                this.client = client;
                return this;
            }

            public void SendNotify() {
                if(client == null) throw new Exception("客户端不可为空");
                var req = new ReqInfo(modIdStr, reqPath, argTypes, args);
                req.IsNotify = true;
                var MSBin = MSBinSerializer.Instance;
                var req2 = (ReqInfo)MSBin.Deserialize(MSBin.Serialize(req));
                client.SendAsync(req, this.ReturnHandler);
            }
            public object SendReq(out RespInfo resp) {
                if(client == null) throw new Exception("客户端不可为空");
                var req = new ReqInfo(modIdStr, reqPath, argTypes, args);
                return client.Send(req, out resp);
            }
            public object SendReq() {
                return SendReq(out _);
            }
            public void SendReqAsync() {
                if(client == null) throw new Exception("客户端不可为空");
                var req = new ReqInfo(modIdStr, reqPath, argTypes, args);
                client.SendAsync(req, this.ReturnHandler);
            }

        }

        protected class ReqInfoPackage {
            public long ticks;
            public ReqInfo req;
            public ManualResetEvent waitEvent;
            public Action<RespInfo> respHendler;
            public RespInfo resp;
        }
    }
}
